/*
 * =====================================================================================
 *
 *       Filename:  cl_server_socket.cxx
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  03/24/2011 12:52:28 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Ken Chen (Ken), chen_kenz@emc.com
 *        Company:  EMC
 *
 * =====================================================================================
 */

#ifndef __GXX_EXPERIMENTAL_CXX0X__
#define __GXX_EXPERIMENTAL_CXX0X__
#endif 

#include <sys/socket.h>
#include <netinet/in.h>
#include <cassert>
#include <cstring>
#include <functional>
#include "cl_server_socket.hxx"
#include "cl_socket_addr_in4.hxx"
#include "cl_socket_addr_factory.hxx"
#include "cl_event_looper.hxx"
#include "cl_channel.hxx"
#include "cl_resource_guard.hxx"
#include "cl_logger.hxx"

namespace neo
{
using namespace std;

namespace
{
typedef union
{
  struct sockaddr sa;
  struct sockaddr_in sa_in;
#ifdef CL_USE_IPV6
  struct sockaddr_in6 sa_in6;
  struct sockaddr_storage sa_stor;
#endif // CL_USE_IPV6
}SockAddr;

} // namespace

CLServerSocket::CLServerSocket(CLEventLooper* looper,
                               const string& host, 
                               int port, 
                               CLSocketType type)
  : CLSocket(host, port, type),
  m_listening(false),
  m_eventLooper(looper),
  m_acceptChannel(new CLChannel(m_socketfd, CLChannel::CL_READ_EVENT)) 
{
  setBlock(false);
  setReuseable(true);
  bind();
  m_acceptChannel->setReadCallback(std::bind(&CLServerSocket::accept, this));
}

CLServerSocket::~CLServerSocket()
{
  // Remove the channel from event looper ?
  // Whose lifetime is longer(CLServerSocket or CLEventLooper) ?
}

void CLServerSocket::setNewConnCallback(const NewConnCallback& cb) 
{
  m_connCallback = cb;
}

bool CLServerSocket::bind(void) 
{
  if(::bind(m_socketfd, m_sockAddr->address(), m_sockAddr->size()) < 0)
  {
	  char errmsg[512];
    CLLOG_C("NET", "CLServerSocket::bind: %d %s", 
				errno, strerror_r(errno, errmsg, sizeof(errmsg)));
    return false;
  }
  return true;
}

bool CLServerSocket::listen(int listenBacklogSize) 
{
  assert(m_eventLooper != NULL);
  if(::listen(m_socketfd, listenBacklogSize) < 0)
  {
	  char errmsg[512];
    CLLOG_C("NET", "CLServerSocket::listen: %d %s", 
				errno, strerror_r(errno, errmsg, sizeof(errmsg)));
  	return false;
  }
  m_eventLooper->addChannel(m_acceptChannel.get());
  m_listening  = true;
  return true;
}

void CLServerSocket::accept(void)  
{
  int connfd = 0;
  SockAddr sockAddr;
  socklen_t sockLen = sizeof(sockAddr); 
  if((connfd = ::accept(m_socketfd, &sockAddr.sa, &sockLen)) < 0)
  {
	  char errmsg[512];
    CLLOG_C("NET", "CLServerSocket::accept: %d %s", 
				errno, strerror_r(errno, errmsg, sizeof(errmsg)));
    return;
  }

  if(!m_connCallback)
  {
    // If not register call back for new connection,
    // just close it.
    ::close(connfd);
  }
  else
  {
    CLSocketAddr* peerAddr(CLSocketAddrFactory::create(&sockAddr.sa));
    if(peerAddr != NULL) 
    {
      m_connCallback(connfd, move(CLSocketAddrUPtr(peerAddr)));
    }
  }
}

bool CLServerSocket::listening(void) const
{
  return m_listening;
}

} // neo
